Skip to content

fix(spp_api_v2_change_request): fix 3 bugs and promote to Beta#13

Open
emjay0921 wants to merge 1 commit into19.0from
fix/spp-api-v2-change-request-bugs
Open

fix(spp_api_v2_change_request): fix 3 bugs and promote to Beta#13
emjay0921 wants to merge 1 commit into19.0from
fix/spp-api-v2-change-request-bugs

Conversation

@emjay0921
Copy link
Contributor

@emjay0921 emjay0921 commented Feb 6, 2026

Why is this change needed?

Three bugs were discovered during curl testing of the Change Request API:

  1. All 8 reference-based endpoints return 404 because CR references (CR/2026/00001) contain slashes that Starlette splits into separate path segments
  2. FastAPI dispatcher's flush_all() triggers mail.thread message_partner_ids writes as Public user (uid=3), causing 403 AccessError
  3. Detail serialization leaks internal mail.thread fields (message_is_follower, has_message, etc.) in API responses

How was the change implemented?

  • Path routing: Replaced /{reference} with /{p1}/{p2}/{p3} on all 8 route definitions + added _build_reference() helper to reconstruct the reference
  • mail.thread AccessError: Added mail_create_nolog=True and tracking_disable=True context to service env in __init__, preventing flush_all() from triggering mail.thread operations
  • Detail serialization: Added 13 mail.thread fields to the skip_fields set in _serialize_detail
  • Promoted development_status from Alpha to Beta
  • Added emjay0921 as maintainer

New unit tests

Unit tests executed by the author

Ran spp_api_v2_change_request test suite: 21 tests, 0 failed, 0 errors

How to test manually

  1. Get OAuth token: POST /api/v2/spp/oauth/token
  2. CREATE: POST /api/v2/spp/ChangeRequest — verify success
  3. READ: GET /api/v2/spp/ChangeRequest/CR/2026/NNNNN — verify returns data (was 404)
  4. SEARCH: GET /api/v2/spp/ChangeRequest?status=draft — verify still works
  5. UPDATE: PUT /api/v2/spp/ChangeRequest/CR/2026/NNNNN
  6. Actions: $submit, $approve, $reject, $request-revision, $apply, $reset
  7. Verify detail in responses has no message_* or has_message fields

Related links

@gemini-code-assist
Copy link

Summary of Changes

Hello @emjay0921, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed!

This pull request significantly improves the robustness and functionality of the Change Request API. It resolves critical issues related to URL routing for complex references, prevents an AccessError during record creation by adjusting the Odoo environment context, and cleans up API responses by filtering out internal mail.thread fields. These changes collectively enhance the API's reliability and user experience, marking a progression in its development lifecycle.

Highlights

  • Path Routing Fix: Addressed 404 errors for Change Request (CR) references containing slashes by changing path parameters from a single "/{reference}" to "/{p1}/{p2}/{p3}" across 8 API endpoints. A helper function _build_reference was introduced to reconstruct the full reference.
  • Mail.thread AccessError Fix: Resolved an AccessError by adding mail_create_nolog=True and tracking_disable=True to the service environment context, preventing unauthorized writes by the Public user (uid=3) during flush_all() operations.
  • Detail Serialization Fix: Enhanced API response consistency by explicitly excluding mail.thread related fields (e.g., message_ids, has_message) from Change Request detail objects during serialization.
  • Development Status Promotion: The module's development status has been updated from "Alpha" to "Beta" in the manifest file, indicating a progression in its maturity.
Changelog
  • spp_api_v2_change_request/manifest.py
    • Updated the development_status from "Alpha" to "Beta".
  • spp_api_v2_change_request/routers/change_request.py
    • Introduced a new helper function _build_reference to reconstruct full CR references from path segments.
    • Modified 8 API endpoints (GET, PUT, and various POST actions like submit, approve, reject, request-revision, apply, reset) to accept CR references as three separate path parameters (p1, p2, p3) instead of a single reference parameter.
    • Integrated the _build_reference helper into these endpoints to reassemble the full reference for service calls.
  • spp_api_v2_change_request/services/change_request_service.py
    • Modified the __init__ method of ChangeRequestService to initialize the Odoo environment with mail_create_nolog=True and tracking_disable=True in its context.
    • Extended the _serialize_detail method to explicitly skip a comprehensive list of mail.thread related fields during serialization, ensuring they are not included in API responses.
Activity
  • No specific activity (comments, reviews, etc.) has been recorded for this pull request yet.
Using Gemini Code Assist

The full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips.

Invoking Gemini

You can request assistance from Gemini at any point by creating a comment using either /gemini <command> or @gemini-code-assist <command>. Below is a summary of the supported commands on the current page.

Feature Command Description
Code Review /gemini review Performs a code review for the current pull request in its current state.
Pull Request Summary /gemini summary Provides a summary of the current pull request in its current state.
Comment @gemini-code-assist Responds in comments when explicitly tagged, both in pull request comments and review comments.
Help /gemini help Displays a list of available commands.

Customization

To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a .gemini/ folder in the base of the repository. Detailed instructions can be found here.

Limitations & Feedback

Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for Github and other Google products, sign up here.

You can also get AI-powered code generation, chat, as well as code reviews directly in the IDE at no cost with the Gemini Code Assist IDE Extension.

Footnotes

  1. Review the Privacy Notices, Generative AI Prohibited Use Policy, Terms of Service, and learn how to configure Gemini Code Assist in GitHub here. Gemini can make mistakes, so double check it and use code with caution.

- Fix path routing: replace /{reference} with /{p1}/{p2}/{p3} on all 8
  reference-based endpoints since CR references (CR/2026/00001) contain
  slashes that Starlette splits into separate path segments
- Fix mail.thread AccessError: add mail_create_nolog and tracking_disable
  context to service env, preventing flush_all() from triggering
  message_partner_ids writes as Public user (uid=3)
- Fix detail serialization: exclude mail.thread fields (message_ids,
  message_is_follower, has_message, etc.) from API responses
- Promote development_status from Alpha to Beta
@emjay0921 emjay0921 force-pushed the fix/spp-api-v2-change-request-bugs branch from ec8ffc4 to 228ac0c Compare February 6, 2026 07:02
Copy link

@gemini-code-assist gemini-code-assist bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request addresses three bugs, including path routing for CR references with slashes, an AccessError in the service layer, and the exclusion of mail.thread fields from serialization, promoting the module to Beta status. However, the routing fix highlights a lack of granular access control (IDOR) in the API endpoints, and the update logic for change request details is susceptible to mass assignment due to insufficient input filtering. For improved maintainability, consider refactoring repetitive path parameter handling in routers/change_request.py into a single FastAPI dependency and programmatically identifying mail.thread fields in services/change_request_service.py.

Comment on lines +43 to +45
def _build_reference(p1: str, p2: str, p3: str) -> str:
"""Reconstruct CR reference from path segments (e.g., CR/2026/00001)."""
return f"{p1}/{p2}/{p3}"

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

This helper function is a good first step, but its repeated use in each of the 8 updated endpoints leads to significant code duplication. A more idiomatic and maintainable approach in FastAPI is to use a dependency.

You can convert this logic into a dependency that extracts the path parameters and provides the reconstructed reference string directly to the endpoint functions. This will centralize the path-parsing logic and make the endpoint signatures much cleaner.

Here's how you could refactor this:

  1. Define the dependency function (you can rename _build_reference):

    def get_cr_reference_from_path(
        p1: Annotated[str, Path(description="Reference part 1 (e.g., CR)")],
        p2: Annotated[str, Path(description="Reference part 2 (e.g., 2026)")],
        p3: Annotated[str, Path(description="Reference part 3 (e.g., 00001)")],
    ) -> str:
        """Dependency to reconstruct CR reference from path segments."""
        return f"{p1}/{p2}/{p3}"
  2. Use the dependency in your endpoints:

    @change_request_router.get("/{p1}/{p2}/{p3}", response_model=ChangeRequestResponse)
    async def read_change_request(
        reference: Annotated[str, Depends(get_cr_reference_from_path)],
        env: Annotated[Environment, Depends(odoo_env)],
        # ... other dependencies
    ):
        # The `reference` variable is now injected by FastAPI.
        # No need for p1, p2, p3 parameters or the _build_reference call inside.
        service = ChangeRequestService(env)
        cr = service.find_by_reference(reference)
        # ...

Applying this pattern to all affected endpoints will greatly improve the code's readability and reduce redundancy.

Comment on lines +179 to 193
# mail.thread fields
"message_ids",
"message_follower_ids",
"message_partner_ids",
"message_is_follower",
"has_message",
"message_needaction",
"message_needaction_counter",
"message_has_error",
"message_has_error_counter",
"message_attachment_count",
"message_has_sms_error",
"message_main_attachment_id",
"website_message_ids",
}

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

Hardcoding the list of mail.thread fields to skip is effective for now, but it can be brittle. If the mail.thread mixin is updated in a future version of Odoo, this list would need to be manually updated to avoid leaking new fields.

A more robust and maintainable approach is to programmatically retrieve the fields from the mail.thread model. This ensures your exclusion list is always synchronized with the mail.thread definition.

Consider this alternative approach:

# In _serialize_detail method

# Get fields from the model
model_fields = detail._fields

# Base fields to skip
skip_fields = {
    "id",
    "create_uid",
    "create_date",
    "write_uid",
    "write_date",
    "__last_update",
    "display_name",
    "change_request_id",
    "registrant_id",
    "approval_state",
    "is_applied",
}

# Programmatically add all mail.thread fields
mail_thread_fields = self.env["mail.thread"]._fields.keys()
skip_fields.update(mail_thread_fields)

for field_name, field in model_fields.items():
    # ... rest of the method

This makes the code more resilient to changes in the underlying Odoo framework.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant